﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Drawing.Drawing2D;
using ComponentFactory.Krypton.Toolkit;

namespace LED_Editor.Controls
{

    public class TrackDataGridViewColumn : DataGridViewColumn
    {
        public TrackDataGridViewColumn()
        {
            this.ValueType = typeof(object);
            this.HeaderCell = new TrackDataGridViewHeaderCell(); 
           StartColor="StartColor";
           EndColor = "EndColor";
           Duration = "FrameDuration";
           FrameBackColor = Color.FromArgb(110,110,110)  ;
           TotalMilliseconds = 1000 * 60 * 60;
           Scale(InitialScale);
           _tooltip = new ToolTip();

           InitReflectionMembers();
           ReadOnly = true;
        }

    

        ToolTip _tooltip;

        public ToolTip ToolTip
        {
            get { return _tooltip; }
        }

        public override bool Selected
        {
            get
            {
                return true;
            }
            set
            {
                base.Selected = true;
            }
        }

        public override bool ReadOnly
        {
            get
            {
                return true;
            }
            set
            {
                base.ReadOnly = true;
            }
        }
        public override DataGridViewCell CellTemplate
        {
            get
            {
                TrackDataGridViewCell cell = new TrackDataGridViewCell();
                cell.ReadOnly = false;
                cell.Selected = false;
                return cell;
            }
            set
            {

                base.CellTemplate = value;
            }
        }

        

        private bool _dataGridViewEventsSet;
        protected override void OnDataGridViewChanged()
        {
            base.OnDataGridViewChanged();
            if (this.DataGridView != null && !_dataGridViewEventsSet)
            {
                DataGridView.Paint += new PaintEventHandler(DataGridView_Paint);
                DataGridView.MouseDown += new MouseEventHandler(DataGridView_MouseDown);
                DataGridView.MouseMove += new MouseEventHandler(DataGridView_MouseMove);
                DataGridView.MouseUp += new MouseEventHandler(DataGridView_MouseUp);
                DataGridView.Scroll += new ScrollEventHandler(DataGridView_Scroll);
                _dataGridViewEventsSet = true;
            }
        }


        void DataGridView_Scroll(object sender, ScrollEventArgs e)
        {
            ((DataGridView)sender).InvalidateColumn (Index );
        }
        bool _isMouseDown = false;
        bool _isMouseMove = false;

        public bool IsMouseMove
        {
            get { return _isMouseMove; }
            set { _isMouseMove = value; }
        }
        bool _isMouseUp = false;
        void DataGridView_MouseDown(object sender, MouseEventArgs e)
        {
            _isMouseDown = true;
            _isMouseMove = false;
            _isMouseUp = false;
            HandleMouse(e);
        }
        void DataGridView_MouseMove(object sender, MouseEventArgs e)
        {
            _isMouseMove = true;
            HandleMouse(e);
        }
        void DataGridView_MouseUp(object sender, MouseEventArgs e)
        {
            _isMouseDown = true;
            _isMouseMove = false;
            _isMouseUp = true;
            HandleMouse(e);
            _isMouseDown = false;
            
        }
        int previousX = 0;

        public  int VirtualHorizontalScrollingOffset
        {
            get
            {
                return HorizontalScrollingOffset;
            }
            set
            {
                HorizontalScrollingOffset = value;
            }
        }

        public int HorizontalScrollingOffset
        {
            get {
                if (DataGridView == null)
                    return 0;
                return DataGridView.HorizontalScrollingOffset ;
            }
            set {
                if (DataGridView == null)
                    return;
                DataGridView.HorizontalScrollingOffset = value; 
            }
        }


        private void HandleMouse(MouseEventArgs e)
        {
            DataGridView.HitTestInfo info = DataGridView.HitTest(e.X, e.Y);
            if (info.ColumnIndex != this.Index)
            {
                DataGridView.Cursor = Cursors.Default; 
                return;
            }
            if (info.RowIndex < 0)
            {
                DataGridView.Cursor = Cursors.Default;
                if (!_isMouseDown)
                  return;
               

                dataGridViewIndicatorX = e.X;


                int oldMilliseconds = Milliseconds;
                Milliseconds = ZoomHelper.GetMilliseconds(dataGridViewIndicatorX - ColumnX + HorizontalScrollingOffset, ScaleFactor);

                if (previousX!=e.X)
                {
                     previousX = e.X;
                    //************
                    DataGridView.InvalidateColumn(Index);
                    //************
                }
            }
            else
            {
 
            }
        }

        internal int ColumnX
        {
            get {
                if (DataGridView == null)
                    return 0;
                return DataGridView.GetCellDisplayRectangle(this.Index, -1, true).X;
            }
        }
        int dataGridViewIndicatorX = 0;
        int timelineIndicatorX = 0;

        protected int TimelineIndicatorX
        {
            get
            {
                return (int)ZoomHelper.GetX(Milliseconds, ScaleFactor) + ColumnX-HorizontalScrollingOffset;
            }
        }


        private bool NeedDrawIndicator
        {
            get {
                return (_isMouseUp || _isMouseDown) && !_isMouseMove;
            }
        }

        void DataGridView_Paint(object sender, PaintEventArgs e)
        {
            
#if DEBUG
            try
            {
#endif
                if (DataGridView == null)
                    return;

                float x = ZoomHelper.GetX(Milliseconds, ScaleFactor) + ColumnX - HorizontalScrollingOffset ;
                if (x < ColumnX)
                    return;

                int height = ((DataGridView)sender).Height ;
                e.Graphics.DrawLine(GraphicsCache.Instance.BlackPen   , new PointF(x, 0), new PointF(x, height));
#if DEBUG
            }
            catch
            {

            }
#endif
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden ) ]
        public string StartColor { get; set; }
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public string EndColor { get; set; }
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public string Duration { get; set; }
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Color FrameBackColor { get; set; }



        public delegate void SelectedFrameChangedHandler(object sender, SelectedFrameChangedEventArgs e);
        public event SelectedFrameChangedHandler SelectedFrameChanged;

        internal void OnSelectedFrameChanged(object frame)
        {
            if (SelectedFrameChanged != null)
            {
                SelectedFrame = frame;
                SelectedFrameChanged(this.DataGridView, new SelectedFrameChangedEventArgs() { Frame = frame });
            }
        }

        public object SelectedFrame { get; private set; }

        public delegate void FrameEditingHandler(object sender, FrameEditingEventArgs e);
        public event FrameEditingHandler FrameEditing;

        internal bool OnFrameEditing(object frame, string context)
        {
            if (FrameEditing != null)
            {
                FrameEditingEventArgs args = new FrameEditingEventArgs() { Frame = frame, Context = context };
                FrameEditing(this.DataGridView, args);
                return !args.Cancel;
            }
            return false;
        }

        private int _milliseconds = 0;
        public int Milliseconds
        {
            get { return _milliseconds; }
            set
            {
                if (_milliseconds != value)
                {
                    _milliseconds = value;

                    DataGridView.InvalidateColumn(Index);
                    OnMillisecondsChanged();

                }

                if (DataGridView == null || this.Index < 0)
                    return;
                Rectangle rect = this.DataGridView.GetCellDisplayRectangle(this.Index,-1,true );
                if (TimelineIndicatorX - ColumnX > rect.Width)
                {
                    HorizontalScrollingOffset += rect.Width;
                }
            }

        }

        private int _totalMilliseconds;

        public int TotalMilliseconds {
            get { return _totalMilliseconds; }
            set {
                _totalMilliseconds = value;
                if (DataGridView != null)
                {
                    DataGridView.InvalidateColumn(Index);
                }
            }
        }

        public event EventHandler MillisecondsChanged;

        protected void OnMillisecondsChanged()
        {
            if (MillisecondsChanged != null)
            {
                MillisecondsChanged(this, EventArgs.Empty);
                
            }

        }


        public int InitialScale
        {
            get { return ZoomHelper.InnerScale  ; }
            set { ZoomHelper.InnerScale = value   ; }

        }

        int _scaleFactor = 100;

        public int ScaleFactor
        {
            get { return _scaleFactor; }
            set {
                _scaleFactor = value;
                Scale(_scaleFactor);
            }
        }


        public void Scale(int scaleFactor)
        {
            if (DataGridView != null)
            {
                _scaleFactor = scaleFactor;
                int calcWidth = (int)ZoomHelper.GetX(TotalMilliseconds, ScaleFactor);

                Width = calcWidth;
                this.DataGridView.InvalidateColumn(this.Index);
          
            }
        }


        public new int Width
        {
            get
            {
                return Thickness ;
            }
            set
            {
                Thickness = value;
            }
        }

        FieldInfo _bandIsRowInfo;
        FieldInfo _cachedThicknessInfo;
        FieldInfo _bandIndexInfo;
        FieldInfo _thicknessInfo;
        MethodInfo _GetHeightInfo;
        PropertyInfo _MinimumThicknessInfo;
        MethodInfo _AdjustFillingColumnInfo;
        PropertyInfo _ThicknessInternalInfo;
        MethodInfo _OnBandThicknessChangingInfo;





        private void InitReflectionMembers()
        {
            Type type = GetType();

            _bandIsRowInfo = type.GetField("bandIsRow", BindingFlags.Instance | BindingFlags.NonPublic);
            _bandIndexInfo = typeof(DataGridViewBand).GetField("bandIndex", BindingFlags.Instance | BindingFlags.NonPublic  );
            _cachedThicknessInfo = typeof(DataGridViewBand).GetField("cachedThickness", BindingFlags.Instance | BindingFlags.NonPublic);
            _thicknessInfo = typeof(DataGridViewBand).GetField("thickness", BindingFlags.Instance | BindingFlags.NonPublic);
            _GetHeightInfo = type.GetMethod("GetHeightInfo", BindingFlags.Instance | BindingFlags.NonPublic);
            _MinimumThicknessInfo = type.GetProperty ("MinimumThickness", BindingFlags.Instance | BindingFlags.NonPublic);
            _AdjustFillingColumnInfo = typeof(DataGridView).GetMethod("AdjustFillingColumn", BindingFlags.Instance | BindingFlags.NonPublic);
            _ThicknessInternalInfo = type.GetProperty("ThicknessInternal", BindingFlags.Instance | BindingFlags.NonPublic);
            _OnBandThicknessChangingInfo = typeof(DataGridView).GetMethod("OnBandThicknessChanging", BindingFlags.Instance | BindingFlags.NonPublic);


            
        }


        internal int Thickness
        {
            get
            {
                if ((bool)this._bandIsRowInfo.GetValue(this) && ((int)this._bandIndexInfo.GetValue(this) > -1))
                {
                    int num=0;
                    int num2=0;
                    this._GetHeightInfo.Invoke(this,new object []{(int)this._bandIndexInfo.GetValue(this), num, num2});
                    return num;
                }
                return (int)this._thicknessInfo.GetValue(this);
            }
            set
            {
                int minimumThickness = (int)_MinimumThicknessInfo.GetValue(this,null) ;
                if (value < minimumThickness)
                {
                    value = minimumThickness;
                }
                bool flag = true;
                if ((bool)this._bandIsRowInfo.GetValue(this))
                {
                    if ((base.DataGridView != null) && (base.DataGridView.AutoSizeRowsMode != DataGridViewAutoSizeRowsMode.None))
                    {
                        this._cachedThicknessInfo.SetValue(this, value);
                        flag = false;
                    }
                }
                else
                {
                    DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)this;
                    DataGridViewAutoSizeColumnMode inheritedAutoSizeMode = dataGridViewColumn.InheritedAutoSizeMode;
                    if (((inheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.Fill) && (inheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.None)) && (inheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.NotSet))
                    {
                        this._cachedThicknessInfo.SetValue(this, value);
                        flag = false;
                    }
                    else if (((inheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill) && (base.DataGridView != null)) && dataGridViewColumn.Visible)
                    {
                        IntPtr handle = base.DataGridView.Handle;
                        _AdjustFillingColumnInfo.Invoke(this.DataGridView,new object[]{ dataGridViewColumn, value});
                        flag = false;
                    }
                }
                if (flag && ((int)this._thicknessInfo.GetValue(this) != value))
                {
                    if (base.DataGridView != null)
                    {
                       _OnBandThicknessChangingInfo.Invoke(base.DataGridView,null);
                    }
                    _ThicknessInternalInfo.SetValue(this, value,null);
                }
            }
        }

    }

   
    public class SelectedFrameChangedEventArgs : EventArgs
    {
        public object Frame { get; set; }
    }

    public class
        FrameEditingEventArgs : CancelEventArgs
    {
        public object Frame { get; set; }
        public string Context { get; set; }

    }

    public class GraphicsCache
    {
        private static GraphicsCache _instance;
        public static GraphicsCache Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new GraphicsCache(); 
                return _instance;
            }
        }
        private GraphicsCache()
        {
            _brushes = new Hashtable();
            _pens = new Hashtable(); 
        }

        private Pen blackPen;

        public Pen BlackPen
        {
            get
            {
                if (blackPen == null)
                    blackPen = new Pen(Color.Black, 1);
                return blackPen;
            }
        }

        private Pen silverPen;

        public Pen SilverPen
        {
            get
            {
                if (silverPen == null)
                    silverPen = new Pen(Color.Silver , 2);
                return silverPen;
            }
        }

        private Hashtable _brushes;


        public Brush GetCachedBrush(Color color)
        {
            Brush brush = (Brush)_brushes[color];
            if (brush == null)
            {
                brush = new SolidBrush(color);
                _brushes.Add(color, brush);
            }
            return brush;
        }

        private Hashtable _pens;


        public Pen GetCachedPen(Color color)
        {
            Pen pen = (Pen)_pens[color];
            if (pen == null)
            {
                pen = new Pen(color);
                _pens.Add(color, pen);
            }
            return pen;
        }

    }

    public class TrackDataGridViewHeaderCell: DataGridViewColumnHeaderCell
    {
        public TrackDataGridViewHeaderCell():base()
        {
           
        }

        static Size _Size = new Size(40, 0);


        protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates dataGridViewElementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
        {
            base.Paint(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
             
            using (Brush back = new LinearGradientBrush(clipBounds,Color.FromArgb(200,200,200)   ,Color.LightGray,90,false ))
            {
                graphics.FillRectangle(back, clipBounds);    
            }

#if DEBUG
            try
            {
#endif
                _timelineItems.Clear();
                int length = clipBounds.Width;
                

                int numberOfLayers = _layers.Count;
                for (int i = 0; i < numberOfLayers; i++)
                {
                    double width = GetWidthByLayer(i);
                    if (width < 1)
                        continue;

                    int countOfLabels = (int)Math.Ceiling(((double)length / (double)width));
                    for (int j = 0; j < countOfLabels; j++)
                    {
                        double currentX = j * width;

                        int key = (int)(currentX);
                        TimeLineItem item = new TimeLineItem();
                        if (_timelineItems.ContainsKey(key))
                        {

                            item = (TimeLineItem)_timelineItems[key];

                            if (item.Layer < i)
                            {
                                _timelineItems.Remove(key);
                            }
                        }
                        TimeLineItem newItem = new TimeLineItem() { Layer = i, Width = currentX, Value = "" };
                        _timelineItems.Add(key, newItem);
                    }

                }
                Brush brush = GraphicsCache.Instance.GetCachedBrush(Color.Black);

                IEnumerator each = _timelineItems.Values.GetEnumerator();
                int counter = 0;
                while (each.MoveNext())
                {
                    TimeLineItem item = (TimeLineItem)each.Current;

                    double width2 = clipBounds.Width - ((TrackDataGridViewColumn)this.OwningColumn).ColumnX;
                    int floor = (int)Math.Floor((double)DataGridView.HorizontalScrollingOffset / width2);
                    double d = (double)DataGridView.HorizontalScrollingOffset - width2 * floor;

                    double x = item.Width + ((TrackDataGridViewColumn)this.OwningColumn).ColumnX;
                    double width = GetWidthByLayer(item.Layer);
                    if (width >= 5)
                    {
                        double lineHeigth = (double)(item.Layer * 2 + 3f);
                        graphics.DrawLine(pen, new PointF((float)x, clipBounds.Height - (float)(lineHeigth)), new PointF((float)x, clipBounds.Height - 2));
                    }
                    SizeF stringSize = _Size;

                    if (width > 10)
                    {
                        int stringX = (int)(item.Width + DataGridView.HorizontalScrollingOffset);
                        double stringX2 = item.Width + ((TrackDataGridViewColumn)this.OwningColumn).ColumnX;//- ZoomHelper.GetScaledWidth(DataGridView.HorizontalScrollingOffset, ((TrackDataGridViewColumn)this.OwningColumn).ScaleFactor);

                        string timeString = TimeHelper.GetUserFriendlyString(ZoomHelper.GetMilliseconds(stringX, ScaleFactor));
                        stringSize = graphics.MeasureString(timeString, cellStyle.Font);
                        if (width > stringSize.Width )
                        {
                            item.Value = timeString;
                            if (item.Width == 0)
                                graphics.DrawString(item.Value, cellStyle.Font, brush, new PointF((float)stringX2, 2));
                            else
                                graphics.DrawString(item.Value, cellStyle.Font, brush, new PointF((float)stringX2 - (float)(stringSize.Width / 2f), 2));
                        }
                    }
                    counter++;
                }
#if DEBUG
            }
            catch
            {

            }
#endif
            

        }

        private static List<double> _layers = new List<double>()
        {
            0.01,0.1,0.5,1,5,10,30,60,120,600
        };
        private Hashtable _timelineItems = new Hashtable();
        [DebuggerDisplay("Width={Width}")]
        public struct TimeLineItem
        {
            public int Layer { get; set; }
            public double Width { get; set; }
            public string Value { get; set; }
        }
        private string GetUnitByLayer(int layer)
        {
            double value = _layers[layer];

            if (value >= 1 && value < 60)
                return "s";
            else if (value == 60)
                return "m";
            else return string.Empty;

        }
        private float GetTimeUnit(int milliseconds)
        {
            float result = 0f;
            if (milliseconds >= 0 && milliseconds < 1)
            {
                result = milliseconds;
            }
            else if (milliseconds >= 1 && milliseconds < 60)
            {
                result = milliseconds;
            }
            else if (milliseconds >= 60 && milliseconds < 60 * 60 && milliseconds % 60 == 0)
            {
                result = milliseconds / 60;
            }
            else if (milliseconds >= 60 * 60 && milliseconds < 60 * 60 * 24 && milliseconds % (60 * 24) == 0)
            {
                result = milliseconds / (60 * 24);//seconds / (60 * 24);
            }
            else return milliseconds;//(float)Math.Round((double)result, 2);

            return result;

        }

        public int ScaleFactor { get { return ((TrackDataGridViewColumn)this.OwningColumn).ScaleFactor; } }

        private double GetWidthByLayer(int layer)
        {
            int ms = (int)(_layers[layer] * 1000);
            return ZoomHelper.GetX(ms, ScaleFactor);
        }
        static Pen pen = Pens.Gray;


    }
}
